package service

import (
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"poem-server/comm/global"
	"poem-server/initialize/config"
	"poem-server/utils/secret"
	"strconv"
	"time"
)

type jwtService struct {
}

var JwtService = new(jwtService)

// 所有需要颁发token的用户模型都必须实现这个接口
type JwtUser interface {
	GetUid() string
}

// CustomClaims 自定义Claims
type CustomClaims struct {
	jwt.StandardClaims
}

const (
	TokenType    = "bearer"
	AppGuardName = "app"
)

type TokenOutPut struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int    `json:"expires_in"`
	TokenType   string `json:"token_type"`
}

// 生成token
// CreateToken 方法需要接收一个 JwtUser 实例对象，
// 我们需要将 app/models/user.go 用户模型实现 JwtUser 接口，
// 后续其他的用户模型都可以通过实现 JwtUser 接口，
// 来调用 CreateToken() 颁发 Token
func (s *jwtService) CreateToken(GuardName string, user JwtUser) (tokenData TokenOutPut, err error) {
	token := jwt.NewWithClaims(
		jwt.SigningMethodHS256,
		CustomClaims{
			StandardClaims: jwt.StandardClaims{
				ExpiresAt: time.Now().Unix() + config.Config.Jwt.JwtTtl,
				Id:        user.GetUid(), // 这里就把userId存入到 相当于tokenId了
				Issuer:    GuardName,     // 用于在中间件中区分不同客户端颁发的token ,避免同一个token跨端使用
				NotBefore: time.Now().Unix() - 1000,
			},
		},
	)
	tokenStr, err := token.SignedString([]byte(config.Config.Jwt.Secret))
	tokenData = TokenOutPut{
		tokenStr,
		int(config.Config.Jwt.JwtTtl),
		TokenType,
	}

	return
}

// 获取黑名单缓存key
func (s *jwtService) getBlackListKey(tokenStr string) string {
	return "jwt_black_list" + secret.MD5([]byte(tokenStr))
}

// 加入黑名单
func (s *jwtService) JoinBlackList(token *jwt.Token) (err error) {
	nowUnix := time.Now().Unix()
	timer := time.Duration(token.Claims.(*CustomClaims).ExpiresAt-nowUnix) * time.Second
	// 将token剩余时间设置为缓存有效时期，并将当前时间作为缓存value值
	result, err := global.App.Redis.SetNX(JwtService.getBlackListKey(token.Raw), nowUnix, timer).Result()
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(result)
	return
}

func (s *jwtService) IsBlacklist(tokenStr string) bool {
	joinUnixStr, err := global.App.Redis.Get(JwtService.getBlackListKey(tokenStr)).Result()
	joinUnix, err := strconv.ParseInt(joinUnixStr, 10, 64)
	if err != nil || joinUnixStr == "" {
		return false
	}
	// JwtBlacklistGracePeriod 为黑名单宽限时间，避免并发请求失效
	if time.Now().Unix()-joinUnix < config.Config.Jwt.JwtBlacklistGracePeriod {
		return false
	}
	return true
}
